<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>处理this</title>
</head>

<body>
    <button class="btn">按钮</button>
    <p>普通函数的调用方式决定了 this 的值，即【谁调用 this 的值指向谁】，如下代码所示：</p>

    <p>箭头函数中的 this 与普通函数完全不同，也不受调用方式的影响，事实上箭头函数中并
        不存在 this ！箭头函数中访问的 this 不过是箭头函数所在作用域的 this 变量。</p>

    <p>改变this指向：<br>

        以上归纳了普通函数和箭头函数中关于 this 默认值的情形，不仅如此 JavaScript 中
        还允许指定函数中 this 的指向，有 3 个方法可以动态指定普通函数中 this 的指向：
    </p>

    <p>
        call <br>
        使用 call 方法调用函数，同时指定函数中 this 的值，使用方法如下代码所示：
    </p>
    <p>
        总结：<br>
        1. call 方法能够在调用函数的同时指定 this 的值<br>
        2. 使用 call 方法调用函数时，第1个参数为 this 指定的值<br>
        3. call 方法的其余参数会依次自动传入函数做为函数的参数

    </p>
    <p>
        apply: <br>

        使用 call 方法调用函数，同时指定函数中 this 的值，使用方法如下代码所示：

    </p>
    <script>

        // 普通函数的调用方式决定了 this 的值，即【谁调用 this 的值指向谁】，如下代码所示：

        // 普通函数
        // function sayHi() {
        //     a: '嗨，你好！'
        //     console.log(this)
        // }
        // // 函数表达式
        // const sayHello = function () {
        //     console.log(this)
        // }
        // // 函数的调用方式决定了 this 的值
        // sayHi() // window
        // window.sayHi() // window
        // sayHello() // window
        // // 普通对象
        // const user = {
        //     name: '小明',
        //     walk: function () {
        //         console.log(this)
        //     }
        // }
        // // 动态为 user 添加方法
        // user.sayHi = sayHi
        // // 函数调用方式，决定了 this 的值
        // user.sayHi() // {name: '小明', walk: ƒ, sayHi: ƒ}




        console.log(this) // 此处为 window
        // 箭头函数
        const sayHi = function () {
            console.log(this) // 该箭头函数中的 this 为函数声明环境中 this 一致
        }
        // 普通对象
        const user = {
            name: '小明',
            // 该箭头函数中的 this 为函数声明环境中 this 一致
            walk: () => {
                console.log(this)  // window
            },

            sleep: function () {
                let str = 'hello'
                console.log(this) // {name: '小明', walk: ƒ, sleep: ƒ, sayHi: ƒ}
                let fn = () => {
                    console.log(str) // hello
                    console.log(this) //{name: '小明', walk: ƒ, sleep: ƒ, sayHi: ƒ},
                    //  该箭头函数中的 this 与 sleep 中的 this 一致
                }
                // 调用箭头函数
                fn();
            }
        }
        // 动态添加方法
        user.sayHi = sayHi
        // 函数方法调用
        user.sayHi()
        user.sleep()
        user.walk()



        // 在开发中【使用箭头函数前需要考虑函数中 this 的值】，事件回调函数使用箭头函数时，
        // this 为全局的 window，因此DOM事件回调函数不推荐使用箭头函数，如下代码所示：
        // DOM 节点
        const btn = document.querySelector('.btn')
        // 箭头函数 此时 this 指向了 window
        btn.addEventListener('click', () => {
            console.log(this) // window
        })
        // 普通函数 此时 this 指向了 DOM 对象
        btn.addEventListener('click', function () {
            console.log(this)
        })



        // 同样由于箭头函数 this 的原因，基于原型的面向对象也不推荐采用箭头函数，如下代码所示：
        function Person() {
        }
        // 原型对象上添加了箭头函数
        Person.prototype.walk = () => {
            console.log('人都要走路...')
            console.log(this); // window
        }
        const p1 = new Person()
        p1.walk()


        // call
        // 使用 call 方法调用函数，同时指定函数中 this 的值，使用方法如下代码所示：

        let user1 = {
            name: '小明',
            age: 18,

        }

        let student = {
            name: '小红',
            age: 16
        }

        // 调用函数并指定 this 的值
        sayHi.call(user1); // this 值为 user1
        sayHi.call(student); // this 值为 student

        // 求和函数
        function counter(x, y) {
            return x + y;
        }

        // 调用 counter 函数，并传入参数
        let result = counter.call(null, 5, 10);
        console.log(result); // 15













    </script>
</body>

</html>